#pragma once
#include "cgzITask.h"
#include "cgzCMongo.h"
#include "cgzCHttpS.h"

extern "C"
{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}

#include <cpprest/json.h>

enum egzl_type
{
    egzl_nil,
    egzl_bool,
    egzl_int,
    egzl_long,
    egzl_llong,
    egzl_float,
    egzl_double,
    egzl_charptr,
    egzl_lstring,
    egzl_jsonstr,
    egzl_voidptr,
    egzl_1array,
    egzl_2array,
    egzl_redisreply,
    egzl_cpprestjson,
    egzl_mongocvalue,
    egzl_mongocarray,
};

union ugzl_value
{
    bool bv;
    int64_t iv;
    uint64_t uiv;
    long lv;
    long long llv;
    float fv;
    double dv;
    const char *cv;
    const void *pv;
};

class clarg;
using clargs = std::vector<clarg>;
using clargs2 = std::vector<clargs>;
class cgzCRedisReply;
class redisReply;
class cgzCMysqlReply;
class cgzClickHouseReply;
class cgzCHttpExecC;
class cgzCHttpRecvS;
class cgzCLoopS;
class cgzCLoopC;
class cgzCLua;

struct cgzCSqlite;
// 情况1：仅需要指针/引用时
namespace clickhouse
{
    class Block; // 前向声明
}

using blockUPtr = std::unique_ptr<clickhouse::Block>;
using SqliteMap = std::unordered_map<std::string, cgzCSqlite *>;

class clarg
{
public:
    friend class cgzCLua;
    clarg(void *av);
    clarg(bool av);
    clarg(int av);
    clarg(long av);
    clarg(long long av);
    clarg(float av);
    clarg(double av);
    clarg(const char *av);
    clarg(const void *av);
    clarg(const char *av, const int len);
    clarg(const char *av, const int len, egzl_type type);
    clarg(clargs &array);
    clarg(clargs2 &array, egzl_type type);
    clarg(const web::json::value &js);
    clarg(const redisReply &reply);
    clarg(const bsoncxx::document::value &mg);
    clarg(const std::vector<bsoncxx::document::value> &mgs);

private:
    int lenght;
    egzl_type type;
    ugzl_value value;
};

class cgzCLua : public cgzITask
{
public:
    cgzCLua(cgzCThreadPool *pool, const char *name, const char *mode, const char *sid);
    ~cgzCLua();

public:
    bool run(int64_t nms) override;
    bool run(const cgzITask *iTask, cgzCRedisReply *pnode);
    bool run(const cgzITask *iTask, cgzCMysqlReply *pnode);
    bool run(const cgzITask *iTask, cgzClickHouseReply *pnode);
    bool run(const cgzITask *iTask, cgzCMongoReply *pnode);
    bool run(const cgzITask *iTask, cgzCHttpExecC *pnode);
    bool run(const cgzITask *iTask, cgzCHttpLoopS *pnode);
    bool run(const cgzITask *iTask, cgzCLoopS *pnode);
    bool run(const cgzITask *iTask, cgzCLoopC *pnode);
    bool runws(const cgzITask *iTask, cgzCLoopS *pnode);
    bool runwc(const cgzITask *iTask, cgzCLoopC *pnode);
    bool rungs(const cgzITask *iTask, cgzCLoopS *pnode);
    bool rungc(const cgzITask *iTask, cgzCLoopC *pnode);

public:
    bool shutdown() override;
    bool mabystop() override;

public:
    bool todoret(cgzIData *cgzTask);

public:
    void reload(const char *fpatch);
    void reload();
    int lpushargs(const clargs &args);
    int lpushargs(lua_State *L, const clarg &arg);
    // 全局函数
    void fcall(const char *fname, const clargs &args, int rargs = 0);
    int rifcall(const char *fname, const clargs &args);
    int rimcall(const char *mname, const char *fname, const clargs &args);
    int riocall(int oref, const char *fname, const clargs &args);
    // 文件函数
    void mcall(const char *mname, const char *fname, const clargs &args, int rargs = 0);
    // 上值函数
    void mcall(const char *mname, const clargs &args, int rargs = 0);
    bool rbmcall(const char *mname, const clargs &args);
    // 成员函数
    void ocall(int oref, const char *fname, const clargs &args, int rargs = 0);
    bool rbfcall(const char *fname, const clargs &args);
    bool rbmcall(const char *mname, const char *fname, const clargs &args);
    bool rbocall(int oref, const char *fname, const clargs &args);
    std::string rsocall(int oref, const char *fname, const clargs &args);

    // 调用单例对象成员函数
    void scall(const char *sname, const char *fname, const clargs &args, int rargs = 0);
    int riscall(const char *sname, const char *fname, const clargs &args);

private:
    void cgzL_openlibs();

public:
    static cgzCLua *getThat(lua_State *L);

public:
    void json2table(const web::json::value &jsonValue);
    void redis2table(const redisReply &reply);
    void bson2table(const bsoncxx::document::view &doc_view);
    void bson2array(const bsoncxx::array::value &doc_arr);
    static void table2bson(bsoncxx::builder::stream::document &doc, lua_State *L);

private:
    static int luaopen_other(lua_State *L);
    static int luaopen_mset(lua_State *L);
    static int luaopen_tcpC(lua_State *L);
    static int luaopen_tcpAsioC(lua_State *L);
    static int luaopen_tcpAsioS(lua_State *L);
    static int luaopen_tcpS(lua_State *L);
    static int luaopen_tcpGateC(lua_State *L);
    static int luaopen_tcpGateS(lua_State *L);
    static int luaopen_tcpGateWC(lua_State *L);
    static int luaopen_tcpGateWS(lua_State *L);
    static int luaopen_tcpWC(lua_State *L);
    static int luaopen_tcpWS(lua_State *L);
    static int luaopen_hiredis(lua_State *L);
    static int luaopen_mysql(lua_State *L);
    static int luaopen_mongo(lua_State *L);
    static int luaopen_sqlite3(lua_State *L);
    static int luaopen_httpC(lua_State *L);
    static int luaopen_httpS(lua_State *L);
    static int luaopen_Spdlog(lua_State *L);
    static int luaopen_clickHouse(lua_State *L);

public:
    lua_State *L = nullptr;
    blockUPtr block;
    SqliteMap sqliteMap;

private:
    std::string m_mode;
    std::string m_patch;
    int8_t m_sid;
};

#define lua_cgz_newt(L)          \
    lua_getglobal(L, "require"); \
    lua_pushstring(L, "newt");   \
    lua_call(L, 1, 1);           \
    lua_call(L, 0, 1)

#define lua_cgz_newa(L, narr)    \
    lua_getglobal(L, "require"); \
    lua_pushstring(L, "newt");   \
    lua_call(L, 1, 1);           \
    lua_pushinteger(L, narr);    \
    lua_call(L, 1, 1)

#define lua_cgz_newh(L, nrec)    \
    lua_getglobal(L, "require"); \
    lua_pushstring(L, "newt");   \
    lua_call(L, 1, 1);           \
    lua_pushinteger(L, 0);       \
    lua_pushinteger(L, nrec);    \
    lua_call(L, 2, 1)
